﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область ПрограммныйИнтерфейс

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции по выполнению проверок ведению учета и получению их результатов.

// Выполняет указанную проверку ведения учета с заданными параметрами.
//
// Параметры:
//   Проверка                    - СправочникСсылка.ПравилаПроверкиУчета
//                               - Строка - правило проверки,
//                                 которая будет выполняться либо строковый идентификатор указанного правила.
//   ПараметрыВыполненияПроверки - Структура
//                               - Массив - произвольные дополнительные параметры проверки,
//                                 которые уточняют, что и как именно проверять. 
//                                 См. КонтрольВеденияУчета.ПараметрыВыполненияПроверки.
//                               - Структура:
//       * Свойство1 - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - первое значение параметра.
//       * Свойство2 - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - второе значение параметра.
//       * Свойство3 - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - третье значение параметра.
//       ...                                              
//     - Массив - несколько параметров проверки (элементы массива типа Структура, как описано выше). 
//   ПроверяемыеОбъекты - ЛюбаяСсылка - если передано, то проверка будет выполнена только для данного объекта.
//                                     Проверка должна поддерживать выборочную проверку и у нее должно быть
//                                     установлено свойство ПоддерживаетВыборочнуюПроверку в значение Истина.
//                                     см. КонтрольВеденияУчетаПереопределяемый.ПриОпределенииПроверок.
//                      - Массив - ссылки на объекты, которые требуется проверить.
//
// Пример:
//   1. Проверка = КонтрольВеденияУчета.ПроверкаПоИдентификатору("ПроверитьСсылочнуюЦелостность");
//      КонтрольВеденияУчета.ВыполнитьПроверку(Проверка);
//   2. ПараметрыВыполненияПроверки = Новый Массив;
//      Параметр1 = КонтрольВеденияУчета.ПараметрыВыполненияПроверки("ЗакрытиеМесяца", Организация1, ЗакрываемыйМесяц);
//      ПараметрыВыполненияПроверки.Добавить(Параметр1);
//      Параметр2 = КонтрольВеденияУчета.ПараметрыВыполненияПроверки("ЗакрытиеМесяца", Организация2, ЗакрываемыйМесяц);
//      ПараметрыВыполненияПроверки.Добавить(Параметр2);
//      ВыполнитьПроверку("ПроверитьПроведениеДокументов", ПараметрыВыполненияПроверки);
//
Процедура ВыполнитьПроверку(Знач Проверка, Знач ПараметрыВыполненияПроверки = Неопределено, ПроверяемыеОбъекты = Неопределено) Экспорт
	
	Если ТипЗнч(Проверка) = Тип("Строка") Тогда
		ВыполняемаяПроверка = ПроверкаПоИдентификатору(Проверка);
		Если ВыполняемаяПроверка.Пустая() Тогда
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Проверка ведения учета с идентификатором ""%1"" не существует (см. %2)'"),
				Проверка,
				"КонтрольВеденияУчетаПереопределяемый.ПриОпределенииПроверок");
		КонецЕсли;
	Иначе
		ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("КонтрольВеденияУчета.ВыполнитьПроверку", "Проверка",
			Проверка, Тип("СправочникСсылка.ПравилаПроверкиУчета"));
		ВыполняемаяПроверка = Проверка;
	КонецЕсли;
	
	Если ПараметрыВыполненияПроверки <> Неопределено Тогда
		ПроверитьПараметрыВыполненияПроверки(ПараметрыВыполненияПроверки, "КонтрольВеденияУчета.ВыполнитьПроверку");
	КонецЕсли;
	
	КонтрольВеденияУчетаСлужебный.ВыполнитьПроверку(ВыполняемаяПроверка, ПараметрыВыполненияПроверки, ПроверяемыеОбъекты);
	
КонецПроцедуры

// Выполняет проверки по заданному контексту - общему признаку, связывающему воедино пакет проверок.
// Если указанный признак установлен у группы проверок, то выполняются все проверки этой группы. 
// В этом случае наличие (или отсутствие) указанного признака у самой проверки значения не имеет.
// Проверки с флагом Использование, установленным в значение Ложь, пропускаются.
//
// Параметры:
//    КонтекстПроверокВеденияУчета - ОпределяемыйТип.КонтекстПроверокВеденияУчета - контекст выполняемых проверок.
//
// Пример:
//    КонтрольВеденияУчета.ВыполнитьПроверкиВКонтексте(Перечисления.ХозяйственныеОперации.ЗакрытиеМесяца);
//
Процедура ВыполнитьПроверкиВКонтексте(КонтекстПроверокВеденияУчета) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("КонтрольВеденияУчета.ВыполнитьПроверкиВКонтексте",
		"КонтекстПроверокВеденияУчета", КонтекстПроверокВеденияУчета,
		Метаданные.ОпределяемыеТипы.КонтекстПроверокВеденияУчета.Тип);
	
	ПроверкиПоКонтексту = КонтрольВеденияУчетаСлужебный.ПроверкиПоКонтексту(КонтекстПроверокВеденияУчета);
	
	ПараметрыМетода        = Новый Соответствие;
	ВерхняяГраницаПроверок = ПроверкиПоКонтексту.ВГраница();
	
	Для ИндексПроверки = 0 По ВерхняяГраницаПроверок Цикл
		МассивПараметров = Новый Массив;
		МассивПараметров.Добавить(ПроверкиПоКонтексту[ИндексПроверки]);
		
		ПараметрыМетода.Вставить(ИндексПроверки, МассивПараметров);
	КонецЦикла;
	
	ИмяПроцедуры = "КонтрольВеденияУчета.ВыполнитьПроверку";
	
	ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияВФоне(Новый УникальныйИдентификатор);
	ПараметрыВыполнения.НаименованиеФоновогоЗадания = НСтр("ru = 'Контроль ведения учета'");
	ПараметрыВыполнения.ОжидатьЗавершение = Неопределено;
	
	РезультатВыполнения = ДлительныеОперации.ВыполнитьПроцедуруВНесколькоПотоков(
		ИмяПроцедуры,
		ПараметрыВыполнения,
		ПараметрыМетода);
	
	Если РезультатВыполнения.Статус <> "Выполнено" Тогда
		Если РезультатВыполнения.Статус = "Ошибка" Тогда
			ТекстОшибки = РезультатВыполнения.ПодробноеПредставлениеОшибки;
		ИначеЕсли РезультатВыполнения.Статус = "Отменено" Тогда
			ТекстОшибки = НСтр("ru = 'Фоновое задание отменено'");
		Иначе
			ТекстОшибки = НСтр("ru = 'Ошибка выполнения фонового задания'");
		КонецЕсли;
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Результаты = ПолучитьИзВременногоХранилища(РезультатВыполнения.АдресРезультата); // Соответствие
	Если ТипЗнч(Результаты) <> Тип("Соответствие") Тогда
		ТекстОшибки = НСтр("ru = 'Фоновое задание не вернуло результат'");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
	Для Каждого ОписаниеРезультата Из Результаты Цикл
		Результат = ОписаниеРезультата.Значение; // см. ДлительныеОперации.ВыполнитьПроцедуру
		Если Результат.Статус <> "Выполнено" Тогда
			Если Результат.Статус = "Ошибка" Тогда
				ТекстОшибки = Результат.ПодробноеПредставлениеОшибки;
			ИначеЕсли Результат.Статус = "Отменено" Тогда
				ТекстОшибки = НСтр("ru = 'Фоновое задание отменено'");
			Иначе
				ТекстОшибки = НСтр("ru = 'Ошибка выполнения фонового задания'");
			КонецЕсли;
			ВызватьИсключение ТекстОшибки;
		КонецЕсли;
	КонецЦикла;
	
	Если ПараметрыМетода.Количество() <> Результаты.Количество() Тогда
		ТекстОшибки = НСтр("ru = 'Не все проверки были выполнены'");
		ВызватьИсключение ТекстОшибки;
	КонецЕсли;
	
КонецПроцедуры

// Возвращает сводку по количеству выявленных проблем указанного вида проверки.
//
// Параметры:
//   ВидПроверок                - СправочникСсылка.ВидыПроверок - ссылка на вид проверки.
//                              - Строка - строковый идентификатор вида проверки.
//                              - Массив из Строка - строковые идентификаторы вида проверки.
//   ПоискПоТочномуСоответствию - Булево - регулирует возможности точности. Если Истина, то поиск ведется
//                                по переданным свойствам на равенство, остальные свойства должны быть равны
//                                Неопределено (табличная часть дополнительных свойств должна быть пуста).
//                                Если Ложь, то значения остальных свойств могут быть произвольными, главное
//                                чтобы соответствующие свойства были равны свойствам структуры. По умолчанию Истина.
//   УчитыватьОтветственного    - Булево - если Истина, то учитываются только проблемы с незаполненным ответственным
//                                и те, за которые ответственный текущий пользователь.
//                                По умолчанию - Ложь.
//
// Возвращаемое значение:
//  Структура:
//    * Количество - Число - общее количество найденных проблем.
//    * ЕстьОшибки - Булево - признак того, имеются ли ошибки среди найденных проблем (с важностью "Ошибка").
//
// Пример:
//   1) Результат = СводнаяИнформацияПоВидамПроверок("СистемныеПроверки");
//   2) ВидПроверок = Новый Массив;
//      ВидПроверок.Добавить("ЗакрытиеМесяца");
//      ВидПроверок.Добавить(Организация);
//      ВидПроверок.Добавить(МесяцЗакрытия);
//      Результат = СводнаяИнформацияПоВидамПроверок(ВидПроверок);
//
Функция СводнаяИнформацияПоВидамПроверок(ВидПроверок = Неопределено, ПоискПоТочномуСоответствию = Истина, УчитыватьОтветственного = Ложь) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.СводнаяИнформацияПоВидамПроверок";
	Если ВидПроверок <> Неопределено Тогда
		ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ВидПроверок", ВидПроверок, КонтрольВеденияУчетаСлужебный.ОписаниеТипаВидПроверки());
	КонецЕсли;
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ПоискПоТочномуСоответствию", ПоискПоТочномуСоответствию, Тип("Булево"));
	
	Возврат КонтрольВеденияУчетаСлужебный.СводнаяИнформацияПоВидамПроверок(ВидПроверок, ПоискПоТочномуСоответствию, УчитыватьОтветственного);
	
КонецФункции

// Возвращает подробные сведения о выявленных проблемах одного или нескольких интересующих видов проверки.
//
// Параметры:
//   ВидПроверок                - СправочникСсылка.ВидыПроверок - ссылка на вид проверки.
//                              - Строка - строковый идентификатор вида проверки.
//                              - Массив из Строка - строковые идентификаторы вида проверки.
//   ПоискПоТочномуСоответствию - Булево - если Истина, то вид проверки определяется по точному соответствию со
//                                всеми переданными значениям свойств в параметре ВидПроверок (см. пример № 2).
//                                Если Ложь, то вид проверки определяется по указанным значениям свойств
//                                и с любыми значениями неуказанных свойств в параметре ВидПроверок (см. пример № 3).
//
// Возвращаемое значение:
//   ТаблицаЗначений:
//     * ПроблемныйОбъект         - ЛюбаяСсылка - ссылка на объект, с которым связана проблема.
//     * ВажностьПроблемы         - ПеречислениеСсылка.ВажностьПроблемыУчета - важность проблемы учета
//                                  "Информация", "Предупреждение", "Ошибка", "ПолезныйСовет" и "ВажнаяИнформация".
//     * ПравилоПроверки          - СправочникСсылка.ПравилаПроверкиУчета - выполненная проверка с описанием проблемы.
//     * ВидПроверок              - СправочникСсылка.ВидыПроверок - вид проверки.
//     * УточнениеПроблемы        - Строка - текстовое уточнение найденной проблемы.
//     * Ответственный            - СправочникСсылка.Пользователи - заполнен, если по выявленной проблеме
//                                  алгоритм проверки определил конкретного ответственного.
//     * Выявлено                 - Дата - дата и время выявления проблемы.
//     * ДополнительнаяИнформация - ХранилищеЗначения - произвольные дополнительные сведения, связанные 
//                                  с выявленной проблемой.
//
// Пример:
//   1) Результат = ПодробнаяИнформацияПоВидамПроверок("СистемныеПроверки");
//   2) ВидПроверок = Новый Массив;
//      ВидПроверок.Добавить("ЗакрытиеМесяца");
//      ВидПроверок.Добавить(Организация);
//      ВидПроверок.Добавить(МесяцЗакрытия);
//      Результат = ПодробнаяИнформацияПоВидамПроверок(ВидПроверок);
//   3) Выбрать все проблемы закрытия месяца по всем периодам для указанной организации:
//      ВидПроверок = Новый Массив;
//      ВидПроверок.Добавить("ЗакрытиеМесяца");
//      ВидПроверок.Добавить(Организация);
//      Результат = ПодробнаяИнформацияПоВидамПроверок(ВидПроверок, Ложь); 
//
Функция ПодробнаяИнформацияПоВидамПроверок(ВидПроверок, ПоискПоТочномуСоответствию = Истина) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.ПодробнаяИнформацияПоВидамПроверок";
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ВидПроверок", ВидПроверок, КонтрольВеденияУчетаСлужебный.ОписаниеТипаВидПроверки());
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ПоискПоТочномуСоответствию", ПоискПоТочномуСоответствию, Тип("Булево"));
	
	Возврат КонтрольВеденияУчетаСлужебный.ПодробнаяИнформацияПоВидамПроверок(ВидПроверок, ПоискПоТочномуСоответствию);
	
КонецФункции

// Возвращает проверку по переданному идентификатору.
//
// Параметры:
//   Идентификатор - Строка - строковый идентификатор проверки. Например, "ПроверитьСсылочнуюЦелостность".
//
// Возвращаемое значение: 
//   СправочникСсылка.ПравилаПроверкиУчета - ссылка на проверку или пустая ссылка, 
//      если проверка с таким идентификатором не существует.
//
Функция ПроверкаПоИдентификатору(Идентификатор) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("КонтрольВеденияУчета.ПроверкаПоИдентификатору", "Идентификатор", Идентификатор, Тип("Строка"));
	Возврат КонтрольВеденияУчетаСлужебный.ПроверкаПоИдентификатору(Идентификатор);
	
КонецФункции

// Возвращает количество проблем, выявленных у переданного объекта.
//
// Параметры:
//   ПроблемныйОбъект - ЛюбаяСсылка - объект, для которого нужно вычислить количество проблем.
//
// Возвращаемое значение:
//   Число
//
Функция КоличествоПроблемПоОбъекту(ПроблемныйОбъект) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("КонтрольВеденияУчета.КоличествоПроблемПоОбъекту", "ПроблемныйОбъект",
		ПроблемныйОбъект, ОбщегоНазначения.ОписаниеТипаВсеСсылки());
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ РАЗРЕШЕННЫЕ
	|	КОЛИЧЕСТВО(*) КАК Количество
	|ИЗ
	|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
	|ГДЕ
	|	РезультатыПроверкиУчета.ПроблемныйОбъект = &ПроблемныйОбъект
	|	И НЕ РезультатыПроверкиУчета.ИгнорироватьПроблему");
	Запрос.УстановитьПараметр("ПроблемныйОбъект", ПроблемныйОбъект);
	
	УстановитьПривилегированныйРежим(Истина);
	Результат = Запрос.Выполнить().Выбрать();
	Возврат ?(Результат.Следующий(), Результат.Количество, 0); 
	
КонецФункции

// Вычисляет количество проблем, выявленных переданным правилом проверки.
//
// Параметры:
//   ПравилоПроверки - СправочникСсылка.ПравилаПроверкиУчета - правило, для которого
//                     нужно вычислить количество проблем.
//
// Возвращаемое значение:
//   Число
//
Функция КоличествоПроблемПоПравилуПроверки(ПравилоПроверки) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("КонтрольВеденияУчета.КоличествоПроблемПоПравилуПроверки", "ПравилоПроверки",
		ПравилоПроверки, Тип("СправочникСсылка.ПравилаПроверкиУчета"));
	
	Запрос = Новый Запрос(
		"ВЫБРАТЬ РАЗРЕШЕННЫЕ
		|	КОЛИЧЕСТВО(*) КАК Количество
		|ИЗ
		|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
		|ГДЕ
		|	РезультатыПроверкиУчета.ПравилоПроверки = &ПравилоПроверки
		|	И НЕ РезультатыПроверкиУчета.ИгнорироватьПроблему");
	Запрос.УстановитьПараметр("ПравилоПроверки", ПравилоПроверки);
	
	УстановитьПривилегированныйРежим(Истина);
	Результат = Запрос.Выполнить().Выбрать();
	Если Результат.Следующий() Тогда
		КоличествоПроблем = Результат.Количество;
	Иначе
		КоличествоПроблем = 0;
	КонецЕсли;
	
	Возврат КоличествоПроблем;
	
КонецФункции

// Формирует параметры выполнения проверки для передачи в процедуры и функции ВыполнитьПроверку, ОписаниеПроблемы,
// ВидПроверки и другие.
// Параметры содержат уточнение, для чего именно требуется выполнить проверку,
// например, проверить закрытие месяца для конкретной организации по конкретному периоду.
// Порядок следования параметров учитывается.
//
// Параметры:
//     Параметр1     - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - первый параметр проверки.
//     Параметр2     - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - второй параметр проверки.
//     Параметр3     - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - третий параметр проверки.
//     Параметр4     - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - четвертый параметр проверки.
//     Параметр5     - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - пятый параметр проверки.
//     ДругиеПараметры - Массив - другие параметры проверки (элементы типов ЛюбаяСсылка, Булево, Число, Строка, Дата).
//
// Возвращаемое значение:
//    Структура:
//       * Наименование - Строка - представление вида проверки. 
//       * Свойство1 - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - первый параметр проверки.
//       * Свойство2 - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - второй параметр проверки.
//       * Свойство3 - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - третий параметр проверки.
//       ...                                              
//       * СвойствоН - ЛюбаяСсылка
//                   - Булево
//                   - Число
//                   - Строка
//                   - Дата - последний параметр вида проверки.
//
// Пример:
//     1. Параметры = ПараметрыВыполненияПроверки("СистемныеПроверки");
//     2. Параметры = ПараметрыВыполненияПроверки("ЗакрытиеМесяца", ОрганизацияСсылка, ЗакрываемыйМесяц);
//
Функция ПараметрыВыполненияПроверки(Знач Параметр1, Знач Параметр2 = Неопределено, Знач Параметр3 = Неопределено,
	Знач Параметр4 = Неопределено, Знач Параметр5 = Неопределено, Знач ДругиеПараметры = Неопределено) Экспорт
	
	Возврат КонтрольВеденияУчетаСлужебный.ПараметрыВыполненияПроверки(Параметр1, Параметр2, Параметр3, Параметр4, Параметр5, ДругиеПараметры);
	
КонецФункции

// Очищает результаты предыдущих проверок, оставляя только те проблемы, которые были проигнорированы ранее
// (признак ИгнорироватьПроблему = Истина).
// Для непараметрических проверок предыдущие результаты очищаются автоматически, а затем выполняется алгоритм проверки.
// Для проверок с параметрами предварительную очистку предыдущих результатов следует выполнять явно с помощью
// этой процедуры в самом алгоритме проверки. В противном случае, одна и та же проблема будет регистрироваться 
// многократно при нескольких последовательных запусках проверки.
//
// Параметры:
//     Проверка                    - СправочникСсылка.ПравилаПроверкиУчета - проверка,
//                                   результаты которой необходимо очистить.
//     ПараметрыВыполненияПроверки - см. КонтрольВеденияУчета.ПараметрыВыполненияПроверки
//                                 - Массив    - несколько параметров проверки (элементы массива типа Структура,
//                                               как описано выше).
//
Процедура ОчиститьРезультатыПредыдущихПроверок(Знач Проверка, Знач ПараметрыВыполненияПроверки) Экспорт
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("КонтрольВеденияУчета.ОчиститьРезультатыПредыдущихПроверок", "Проверка",
		Проверка, Тип("СправочникСсылка.ПравилаПроверкиУчета"));
	ПроверитьПараметрыВыполненияПроверки(ПараметрыВыполненияПроверки, "КонтрольВеденияУчета.ОчиститьРезультатыПредыдущихПроверок");
	
	КонтрольВеденияУчетаСлужебный.ОчиститьРезультатыПредыдущихПроверок(Проверка, ПараметрыВыполненияПроверки);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для регистрации проблем ведения учета.

// Формирует описание проблемы для последующей регистрации
// с помощью процедуры КонтрольВеденияУчета.ЗаписатьПроблему в процедуре-обработчике проверки.
//
// Параметры:
//   ПроблемныйОбъект  - ЛюбаяСсылка - объект, с которым связана выявленная проблема.
//   ПараметрыПроверки - Структура - параметры выполняемой проверки, значение которых следует взять из одноименного 
//                                   параметра процедуры-обработчика проверки:
//     * Проверка         - СправочникСсылка.ПравилаПроверкиУчета - выполненная проверку.
//     * ВидПроверки      - СправочникСсылка.ВидыПроверок - вид проверки, к которому относится выполненная проверка.
//     * ВажностьПроблемы   - ПеречислениеСсылка.ВажностьПроблемыУчета - важность, с которой следует
//                            зарегистрировать выявленную проблемы учета:
//                            Информация, Предупреждение, Ошибка, ПолезныйСовет или ВажнаяИнформация.
//     * Идентификатор      - Строка - строковый идентификатор проверки.
//     * ДатаНачалаПроверки - Дата - пороговая дата, обозначающая границу проверяемых
//                            объектов (только для объектов с датой). Объекты, дата которых меньше 
//                            указанной, не следует проверять. По умолчанию не заполнено (т.е. проверять все).
//     * ЛимитПроблем       - Число - количество проверяемых объектов.
//                            По умолчанию 1000. Если указано 0, то следует проверить все объекты.
//     * ВидПроверки        - СправочникСсылка.ВидыПроверок - ссылка на вид проверки, к
//                            которому относится выполненная проверка.
//
// Возвращаемое значение:
//   Структура:
//     * ПроблемныйОбъект         - ЛюбаяСсылка - ссылка на объект-источник проблемы.
//     * Проверка                 - СправочникСсылка.ПравилаПроверкиУчета - ссылка на выполненную проверку.
//                                  Взято из переданной структуры ПараметрыПроверки.
//     * ВидПроверки              - СправочникСсылка.ВидыПроверок - ссылка на вид проверки, к которому относится
//                                  выполненная проверка. Взято из переданной структуры ПараметрыПроверки
//     * ВажностьПроблемы         - СправочникСсылка.ВидыПроверок - ссылка на вид проверки, к которому относится
//                                  выполненная проверка. Взято из переданной структуры ПараметрыПроверки.
//     * УточнениеПроблемы        - Строка - строка уточнения проблемы. По умолчанию не заполнена.
//     * КлючУникальности         - УникальныйИдентификатор - ключ уникальности проблемы.
//     * Выявлено                 - Дата - момент обнаружения проблемы.
//     * ДополнительнаяИнформация - ХранилищеЗначения
//                                - Неопределено - произвольные дополнительные сведения, связанные
//                                  с выявленной проблемой. По умолчанию Неопределено.
//     * Ответственный            - СправочникСсылка.Пользователи
//                                - Неопределено - заполнен, если за проблемный объект 
//                                  имеется конкретный ответственный. По умолчанию Неопределено.
//
// Пример:
//  Проблема = КонтрольВеденияУчета.ОписаниеПроблемы(ПроблемныйДокумент, ПараметрыПроверки);
//  Проблема.ВидПроверки = ВидПроверки;
//  Проблема.УточнениеПроблемы = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
//    НСтр("ru = 'По контрагенту ""%1"" имеется непроведенный документ ""%2""'"), Результат.Контрагент, 
//      ПроблемныйДокумент);
//  КонтрольВеденияУчета.ЗаписатьПроблему(Проблема, ПараметрыПроверки);
//
Функция ОписаниеПроблемы(ПроблемныйОбъект, ПараметрыПроверки) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.ОписаниеПроблемы";
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ПроблемныйОбъект", ПроблемныйОбъект, 
		ОбщегоНазначения.ОписаниеТипаВсеСсылки());
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ПараметрыПроверки", ПараметрыПроверки, Тип("Структура"), 
		КонтрольВеденияУчетаСлужебный.ОжидаемыеТипыСвойствПараметровПроверки());
		
	Возврат КонтрольВеденияУчетаСлужебный.ОписаниеПроблемы(ПроблемныйОбъект, ПараметрыПроверки);
	
КонецФункции

// Записывает результат выполнения проверки.
//
// Параметры:
//   Проблема          - см. КонтрольВеденияУчета.ОписаниеПроблемы.
//   ПараметрыПроверки - см. КонтрольВеденияУчета.ОписаниеПроблемы.ПараметрыПроверки.
//
Процедура ЗаписатьПроблему(Проблема, ПараметрыПроверки = Неопределено) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.ЗаписатьПроблему";
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "Проблема", Проблема, Тип("Структура"), 
		КонтрольВеденияУчетаСлужебный.ОжидаемыеТипыСвойствОписанияПроблемы());
	Если ПараметрыПроверки <> Неопределено Тогда
		ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ПараметрыПроверки", ПараметрыПроверки, Тип("Структура"), 
			КонтрольВеденияУчетаСлужебный.ОжидаемыеТипыСвойствПараметровПроверки());
	КонецЕсли;
	
	КонтрольВеденияУчетаСлужебный.ЗаписатьПроблему(Проблема, ПараметрыПроверки);
	
КонецПроцедуры

// Устанавливает или снимает признак игнорирования проблемы ведения учета. 
// При установке параметра Игнорировать в Истина проблема перестает выводиться пользователям в формах объектов 
// и отчете о результатах проверок. Например, это полезно, если пользователь решил, что 
// обнаруженная проблема не существенна или ей не планируется заниматься.
// При сбросе в значение Ложь проблема вновь становится актуальной.
//
// Параметры:
//   ОписаниеПроблемы             - Структура:
//     * ПроблемныйОбъект         - ЛюбаяСсылка - ссылка на объект, с которым связана проблема.
//     * ПравилоПроверки          - СправочникСсылка.ПравилаПроверкиУчета - выполненная проверка с описанием проблемы.
//     * ВидПроверок              - СправочникСсылка.ВидыПроверок - вид проверки.
//     * УточнениеПроблемы        - Строка - текстовое уточнение найденной проблемы.
//     * ДополнительнаяИнформация - ХранилищеЗначения - дополнительная информация об игнорируемой проблеме.
//   Игнорировать - Булево - устанавливаемое значение для указанной проблемы.
//
Процедура ИгнорироватьПроблему(Знач ОписаниеПроблемы, Знач Игнорировать) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.ИгнорироватьПроблему";
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "Игнорировать", Игнорировать, Тип("Булево"));
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ОписаниеПроблемы", ОписаниеПроблемы, Тип("Структура"), 
		КонтрольВеденияУчетаСлужебный.ОжидаемыеТипыСвойствОписанияПроблемы(Ложь));
	
	КонтрольВеденияУчетаСлужебный.ИгнорироватьПроблему(ОписаниеПроблемы, Игнорировать);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Процедуры и функции для встраивания подсистемы в формы объектов конфигурации.

// В форме списка выводит колонку с картинкой, сигнализирующей о наличии проблем с объектами в строках. 
// Вызывается из события ПриСозданииНаСервере формы списка.
// У динамических списков должна быть определена основная таблица. 
//
// Параметры:
//   Форма                  - ФормаКлиентскогоПриложения - форма списка.
//   ИменаСписков           - Строка - имена динамических списков через запятую.
//   ДополнительныеСвойства - Структура
//                          - Неопределено - дополнительные свойства:
//      * ИмяПоляИндикатораПроблем - Строка - имя поля динамического списка, которое
//                            будет использовано для вывода индикатора
//                            наличия проблем у объекта.
//
Процедура ПриСозданииНаСервереФормыСписка(Форма, ИменаСписков, ДополнительныеСвойства = Неопределено) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.ПриСозданииНаСервереФормыСписка";
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "Форма", Форма, Тип("ФормаКлиентскогоПриложения"));
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ИменаСписков", ИменаСписков, Тип("Строка"));
	ИмяПоляИндикатораПроблем = Неопределено;
	Если ДополнительныеСвойства <> Неопределено Тогда
		ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ДополнительныеСвойства", ДополнительныеСвойства, Тип("Структура"));
		ДополнительныеСвойства.Свойство("ИмяПоляИндикатораПроблем", ИмяПоляИндикатораПроблем);
	КонецЕсли;
	
	Если Не ПодсистемаДоступна() Тогда
		Возврат;
	КонецЕсли;
	
	ГлобальныеНастройки = КонтрольВеденияУчетаСлужебный.ГлобальныеНастройки();
	СписокИмен          = СтрРазделить(ИменаСписков, ",");
	КартинкаЗначений    = БиблиотекаКартинок.КоллекцияВажностьПроблемВеденияУчета;
	
	Для Каждого ИмяСписка Из СписокИмен Цикл
		ТаблицаФормы = Форма.Элементы.Найти(СокрЛП(ИмяСписка));
		Если ТаблицаФормы = Неопределено Тогда
			Продолжить;
		КонецЕсли;
			
		ТекущийСписок   = Форма[ТаблицаФормы.ПутьКДанным];
		ОсновнаяТаблица = ТекущийСписок.ОсновнаяТаблица;
		Если Не ЗначениеЗаполнено(ОсновнаяТаблица) Тогда
			Продолжить;
		КонецЕсли;
			
		ТекстЗапроса = "";
		Если ТекущийСписок.ПроизвольныйЗапрос Тогда
			ТекстЗапроса = ТекущийСписок.ТекстЗапроса;
		Иначе
			ИсполняемаяСхема               = ТаблицаФормы.ПолучитьИсполняемуюСхемуКомпоновкиДанных();
			НаборДанныхДинамическогоСписка = ИсполняемаяСхема.НаборыДанных.Найти("НаборДанныхДинамическогоСписка"); // НаборДанныхЗапросСхемыКомпоновкиДанных
			Если НаборДанныхДинамическогоСписка <> Неопределено Тогда
				ТекущийСписок.ПроизвольныйЗапрос = Истина;
				ТекстЗапроса = НаборДанныхДинамическогоСписка.Запрос;
			КонецЕсли;
		КонецЕсли;
		
		Если Не ЗначениеЗаполнено(ТекстЗапроса) Или Не СтрНачинаетсяС(ТекстЗапроса, "ВЫБРАТЬ") Тогда // @query-part-1
			Продолжить;
		КонецЕсли;
			
		Если ИмяПоляИндикатораПроблем = Неопределено Тогда
			ИмяКолонки = "ИндикаторОшибки_" + ОбщегоНазначения.КонтрольнаяСуммаСтрокой(Форма.ИмяФормы + ПолучитьРазделительПути() + ИмяСписка);
		Иначе
			ИмяКолонки = ИмяПоляИндикатораПроблем;
		КонецЕсли;
		
		РазделенноеИмя = СтрРазделить(ОсновнаяТаблица, ".");
		
		ДополнительныеСвойстваКомпоновщика = ТекущийСписок.КомпоновщикНастроек.Настройки.ДополнительныеСвойства;
		ДополнительныеСвойстваКомпоновщика.Вставить("КолонкаИндикатора",    ИмяКолонки);
		ДополнительныеСвойстваКомпоновщика.Вставить("ВидОбъектаМетаданных", РазделенноеИмя.Получить(0));
		ДополнительныеСвойстваКомпоновщика.Вставить("ИмяОбъектаМетаданных", РазделенноеИмя.Получить(1));
		ДополнительныеСвойстваКомпоновщика.Вставить("ИмяСписка",            ИмяСписка);
		
		Если ИмяПоляИндикатораПроблем = Неопределено Тогда
			СтруктураСвойствДинамическогоСписка = ОбщегоНазначения.СтруктураСвойствДинамическогоСписка();
			ДобавляемоеПоле = "	0 КАК " + ИмяКолонки + ",";
			ЗапросМассивом = СтрРазделить(ТекстЗапроса, Символы.ПС);
			ПозицияДляВставки = Неопределено;
			Если СтрЧислоВхождений(ТекстЗапроса, "ВЫБРАТЬ") > 1 Тогда // @query-part-1
				Индекс = 0;
				Для Каждого СтрокаЗапроса Из ЗапросМассивом Цикл
					Если СтрНачинаетсяС(СокрЛП(СтрокаЗапроса), "ВЫБРАТЬ") Тогда // @query-part-1
						Если ПозицияДляВставки = Неопределено Тогда
							ПозицияДляВставки = Индекс + 1;
						Иначе
							Прервать;
						КонецЕсли;
					ИначеЕсли СтрНачинаетсяС(СокрЛП(СтрокаЗапроса), "ПОМЕСТИТЬ") Тогда
						ПозицияДляВставки = Неопределено;
					КонецЕсли;
					Индекс = Индекс + 1;
				КонецЦикла;
			Иначе
				ПозицияДляВставки = 1;
			КонецЕсли;
			ЗапросМассивом.Вставить(ПозицияДляВставки, ДобавляемоеПоле);
			СтруктураСвойствДинамическогоСписка.ТекстЗапроса = СтрСоединить(ЗапросМассивом, Символы.ПС);
			ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(ТаблицаФормы, СтруктураСвойствДинамическогоСписка);
		КонецЕсли;
		
		ПараметрыКолонкиИндикации = Новый Структура;
		
		КонтрольВеденияУчетаСлужебный.ПриОпределенииПараметровКолонкиИндикации(ПараметрыКолонкиИндикации, ОсновнаяТаблица);
		КонтрольВеденияУчетаПереопределяемый.ПриОпределенииПараметровКолонкиИндикации(ПараметрыКолонкиИндикации, ОсновнаяТаблица);
		
		КолонкаИндикаторОшибки = Форма.Элементы.Добавить(ИмяКолонки, Тип("ПолеФормы"), ТаблицаФормы);
		КолонкаИндикаторОшибки.Вид                = ВидПоляФормы.ПолеКартинки;
		КолонкаИндикаторОшибки.ПутьКДанным        = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("%1.%2", ИмяСписка, ИмяКолонки);
		КолонкаИндикаторОшибки.ПоложениеЗаголовка = ПараметрыКолонкиИндикации.ПоложениеЗаголовка;
		КолонкаИндикаторОшибки.КартинкаШапки      = ГлобальныеНастройки.КартинкаИндикатораПроблем;
		КолонкаИндикаторОшибки.КартинкаЗначений   = КартинкаЗначений;
		КолонкаИндикаторОшибки.Заголовок          = НСтр("ru = 'Индикатор ошибки'");
		
		КолонкиСписка = ТаблицаФормы.ПодчиненныеЭлементы;
		Если КолонкиСписка.Количество() > 0 Тогда
			Если ПараметрыКолонкиИндикации.ВыводитьПоследней Тогда
				Форма.Элементы.Переместить(КолонкаИндикаторОшибки, ТаблицаФормы);
			Иначе
				Форма.Элементы.Переместить(КолонкаИндикаторОшибки, ТаблицаФормы, КолонкиСписка.Получить(0));
			КонецЕсли;
		КонецЕсли;
		
		ТаблицаФормы.УстановитьДействие("Выбор", "Подключаемый_Выбор");
		
	КонецЦикла;
	
КонецПроцедуры

// В форме списка выводит колонку с картинкой, сигнализирующей о наличии проблем с объектами в строках. 
// Вызывается из события ПриПолученииДанныхНаСервере формы списка.
//
// Параметры:
//   Настройки              - НастройкиКомпоновкиДанных - содержит копию полных настроек динамического списка.
//   Строки                 - СтрокиДинамическогоСписка - коллекция содержит данные и оформление всех строк,
//                            получаемых в списке, кроме строк группировок.
//   ИмяКлючевогоПоля       - Строка - "Ссылка" или заданное имя колонки, содержащую ссылку объекта.
//   ДополнительныеСвойства - Структура
//                          - Неопределено - содержит дополнительные свойства в случае
//                            необходимости их использования.
//
Процедура ПриПолученииДанныхНаСервере(Настройки, Строки, ИмяКлючевогоПоля = "Ссылка", ДополнительныеСвойства = Неопределено) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.ПриПолученииДанныхНаСервере";
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "Настройки", Настройки, Тип("НастройкиКомпоновкиДанных"));
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "Строки", Строки, Тип("СтрокиДинамическогоСписка"));
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ИмяКлючевогоПоля", ИмяКлючевогоПоля, Тип("Строка"));
	Если ДополнительныеСвойства <> Неопределено Тогда
		ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ДополнительныеСвойства", ДополнительныеСвойства, Тип("Структура"));
	КонецЕсли;
	
	Если Не ПодсистемаДоступна() Тогда
		Возврат;
	КонецЕсли;
	
	ДополнительныеСвойстваКомпоновщика = Настройки.ДополнительныеСвойства;
	Если ДополнительныеСвойстваКомпоновщика.Свойство("КолонкаИндикатора") Тогда
		
		КолонкаИндикатора = Настройки.ДополнительныеСвойства.КолонкаИндикатора;
		
		Если ИмяКлючевогоПоля = "Ссылка" Тогда
			КлючиСтрок = Строки.ПолучитьКлючи();
			КлючСсылка = Истина;
		Иначе
			НачальныеКлючи = Строки.ПолучитьКлючи();
			КлючСсылка     = ОбщегоНазначения.ЭтоСсылка(Тип(НачальныеКлючи[0]));
			КлючиСтрок     = Новый Массив;
			Для Каждого НачальныйКлюч Из НачальныеКлючи Цикл
				КлючиСтрок.Добавить(НачальныйКлюч[ИмяКлючевогоПоля]);
			КонецЦикла;
		КонецЕсли;
		
		ПроблемныеОбъекты = КонтрольВеденияУчетаСлужебный.ПроблемныеОбъекты(КлючиСтрок, Истина);
		
		Для Каждого КлючСтроки Из КлючиСтрок Цикл
			
			Если КлючСсылка Тогда
				КонтрольВеденияУчетаСлужебный.ЗаполнитьИндексКартинки(Строки, Строки[КлючСтроки], КлючСтроки, КолонкаИндикатора, ПроблемныеОбъекты);
			Иначе
				Для Каждого СтрокаСписка Из Строки Цикл
					Если СтрокаСписка.Ключ[ИмяКлючевогоПоля] = КлючСтроки Тогда
						КонтрольВеденияУчетаСлужебный.ЗаполнитьИндексКартинки(Строки, СтрокаСписка.Значение, КлючСтроки, КолонкаИндикатора, ПроблемныеОбъекты);
					КонецЕсли;
				КонецЦикла;
			КонецЕсли;
			
		КонецЦикла;
		
	КонецЕсли;
	
КонецПроцедуры

// В форме объекта выводит группу с картинкой и надписью, сигнализирующими о наличии проблем с этим объектом. 
// Вызывается из события ПриЧтенииНаСервере формы объекта.
//
// Параметры:
//   Форма         - ФормаКлиентскогоПриложения - форма объекта.
//   ТекущийОбъект - ДокументОбъект - объект, который будет прочитан.
//                 - СправочникОбъект
//                 - ПланОбменаОбъект
//                 - ПланВидовХарактеристикОбъект
//                 - ПланСчетовОбъект
//                 - ПланВидовРасчетаОбъект
//                 - ЗадачаОбъект
//
Процедура ПриЧтенииНаСервере(Форма, ТекущийОбъект) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.ПриЧтенииНаСервере";
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "Форма", Форма, Тип("ФормаКлиентскогоПриложения"));
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ТекущийОбъект", ТекущийОбъект, 
		КонтрольВеденияУчетаСлужебныйПовтИсп.ОписаниеТипаВсеОбъекты());
	
	Если Не ПодсистемаДоступна() Тогда
		Возврат;
	КонецЕсли;
	
	Настройки = КонтрольВеденияУчетаСлужебный.ГлобальныеНастройки();
	Настройки.Вставить("КоличествоПроблем", 0);
	Настройки.Вставить("ТекстПроблемы", "");
	Настройки.Вставить("ВажностьПроблемы", Неопределено);
	Настройки.Вставить("ДатаПоследнейПроверки", Неопределено);
	Настройки.Вставить("ДетальныйВид", Ложь);
	
	СсылкаНаОбъект             = ТекущийОбъект.Ссылка;
	ЭлементыУправляемойФормы   = Форма.Элементы;
	Настройки.КоличествоПроблем = КоличествоПроблемПоОбъекту(СсылкаНаОбъект);
	КлючУникальностиИмен       = ОбщегоНазначения.КонтрольнаяСуммаСтрокой(СсылкаНаОбъект.Метаданные().ПолноеИмя()
		+ ПолучитьРазделительПути() + Форма.ИмяФормы);
		
	ДекорацияГруппа = ЭлементыУправляемойФормы.Найти("ГруппаИндикатораОшибки_" + КлючУникальностиИмен);
	
	ПараметрыГруппыИндикации = Новый Структура;
	КонтрольВеденияУчетаСлужебный.ПриОпределенииПараметровГруппыИндикации(ПараметрыГруппыИндикации, ТипЗнч(СсылкаНаОбъект));
	КонтрольВеденияУчетаПереопределяемый.ПриОпределенииПараметровГруппыИндикации(ПараметрыГруппыИндикации, ТипЗнч(СсылкаНаОбъект));
	
	Если Настройки.КоличествоПроблем = 0 Тогда
		Если ДекорацияГруппа <> Неопределено Тогда
			ЭлементыУправляемойФормы.Удалить(ДекорацияГруппа);
		КонецЕсли;
		Возврат;
	КонецЕсли;
	Настройки.ДетальныйВид = ПараметрыГруппыИндикации.ДетальныйВид И Настройки.КоличествоПроблем = 1;
	Если Настройки.ДетальныйВид Тогда
		ЗаполнитьЗначенияСвойств(Настройки, КонтрольВеденияУчетаСлужебный.СведенияОПроблемеСОбъектом(СсылкаНаОбъект));
	КонецЕсли;
	
	Настройки.ДатаПоследнейПроверки = КонтрольВеденияУчетаСлужебный.ПоследняяПроверкаОбъекта(СсылкаНаОбъект);
	Если ДекорацияГруппа <> Неопределено Тогда
		
		ДекорацияНадпись = ЭлементыУправляемойФормы.Найти("ДекорацияНадпись_" + КлючУникальностиИмен);
		Если ДекорацияНадпись <> Неопределено Тогда
			ДекорацияНадпись.Заголовок = КонтрольВеденияУчетаСлужебный.СформироватьОбщуюСтрокуИндикатор(Форма, СсылкаНаОбъект, Настройки);
		КонецЕсли;
		
	Иначе
		
		ГруппаИндикатораОшибки = КонтрольВеденияУчетаСлужебный.РазместитьГруппуИндикатораОшибки(Форма, КлючУникальностиИмен,
			ПараметрыГруппыИндикации.ИмяРодителяГруппы, ПараметрыГруппыИндикации.ВыводитьСнизу);
		
		ОбщаяСтрокаИндикатор = КонтрольВеденияУчетаСлужебный.СформироватьОбщуюСтрокуИндикатор(Форма, СсылкаНаОбъект, Настройки);
		
		КонтрольВеденияУчетаСлужебный.ЗаполнитьГруппуИндикатораОшибки(Форма, ГруппаИндикатораОшибки, КлючУникальностиИмен,
			ОбщаяСтрокаИндикатор, Настройки);
		
	КонецЕсли;
	
КонецПроцедуры

// Запускает фоновую проверку переданного объекта.
// Выполняются только те проверки, по которым ранее были найдены ошибки и у которых
// установлено свойство ПоддерживаетВыборочнуюПроверку в значение Истина.
// 
// Параметры:
//   ТекущийОбъект - ДокументОбъект - <ВидОбъектаМетаданных>Объект.<ИмяОбъектаМетаданных>.
//                 - СправочникОбъект
//                 - ПланОбменаОбъект
//                 - ПланВидовХарактеристикОбъект
//                 - ПланСчетовОбъект
//                 - ПланВидовРасчетаОбъект
//                 - ЗадачаОбъект
//
Процедура ПослеЗаписиНаСервере(ТекущийОбъект) Экспорт
	Запрос = Новый Запрос;
	Запрос.Текст =
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	РезультатыПроверкиУчета.ПравилоПроверки КАК ПравилоПроверки
		|ИЗ
		|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
		|ГДЕ
		|	РезультатыПроверкиУчета.ПроблемныйОбъект = &ПроблемныйОбъект";
	Запрос.УстановитьПараметр("ПроблемныйОбъект", ТекущийОбъект.Ссылка);
	
	УстановитьПривилегированныйРежим(Истина);
	Результат = Запрос.Выполнить().Выгрузить();
	УстановитьПривилегированныйРежим(Ложь);
	
	Проверки = Результат.ВыгрузитьКолонку("ПравилоПроверки");
	Если Проверки.Количество() = 0 Тогда
		Возврат;
	КонецЕсли;
	
	ДлительныеОперации.ВыполнитьПроцедуру(, "КонтрольВеденияУчетаСлужебный.ПроверитьОбъект", ТекущийОбъект.Ссылка, Проверки);
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Прочие процедуры и функции.

// Возвращает Истина, если есть права на просмотр проблем ведения учета.
//
// Возвращаемое значение:
//   Булево
//
Функция ПодсистемаДоступна() Экспорт
	
	Возврат КонтрольВеденияУчетаСлужебный.ПодсистемаДоступна();
	
КонецФункции

// Возвращает виды проверки по переданным параметрам.
//
// Параметры:
//   ВидПроверок                - Строка
//                              - Массив из Строка 
//                              - СправочникСсылка.ВидыПроверок - строковый идентификатор вида проверки,
//                                либо массив строковых идентификаторов, либо ссылка на вид проверки.
//   ПоискПоТочномуСоответствию - Булево - регулирует возможности точности. Если Истина, то поиск ведется
//                                по переданным свойствам на равенство, остальные свойства должны быть равны
//                                Неопределено (табличная часть дополнительных свойств должна быть пуста).
//                                Если Ложь, то значения остальных свойств могут быть произвольными, главное
//                                чтобы соответствующие свойства были равны свойствам структуры. По умолчанию Истина.
//
// Возвращаемое значение:
//   Массив - элементы СправочникСсылка.ВидыПроверок, либо пустой массив, если поиск не дал результата. 
//            При поиска по точному соответствию массив содержит единственный элемент.
//
Функция ВидыПроверок(ВидПроверок, ПоискПоТочномуСоответствию = Истина) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.ВидыПроверок";
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ВидПроверок", ВидПроверок, КонтрольВеденияУчетаСлужебный.ОписаниеТипаВидПроверки());
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ПоискПоТочномуСоответствию", ПоискПоТочномуСоответствию, Тип("Булево"));
	
	Возврат КонтрольВеденияУчетаСлужебный.ВидыПроверок(ВидПроверок, ПоискПоТочномуСоответствию);
	
КонецФункции

// Возвращает существующий или создает новый элемент справочника ВидыПроверок 
// для регистрации или отбора результатов ведения учета.
//
// Параметры:
//     ПараметрыВыполненияПроверки - Строка - строковый идентификатор вида проверки (Свойство1)
//                                 - Структура - сведения, идентифицирующие вид проверки.
//     ТолькоПоиск - Булево - если Истина и вид проверки с заданными параметрами не существует, 
//                   возвращается пустая ссылка; если Ложь, то создается элемент и возвращается ссылка на него.
//
// Возвращаемое значение:
//   СправочникСсылка.ВидыПроверок - найденный существующий или созданный элемент справочника.
//      В случае если осуществлялся только поиск (параметр ТолькоПоиск = Истина) 
//      и элемент не был найден, возвращается пустая ссылка на справочник СправочникСсылка.ВидыПроверок.
//
// Пример:
//   ВидПроверки = КонтрольВеденияУчета.ВидПроверки("СистемныеПроверки");
//
Функция ВидПроверки(Знач ПараметрыВыполненияПроверки, Знач ТолькоПоиск = Ложь) Экспорт
	
	ДопустимыеТипы = Новый Массив;
	ДопустимыеТипы.Добавить(Тип("Строка"));
	ДопустимыеТипы.Добавить(Тип("Структура"));
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("КонтрольВеденияУчета.ВидПроверки",
		"ПараметрыВыполненияПроверки", ПараметрыВыполненияПроверки, ДопустимыеТипы);
	Если ТипЗнч(ПараметрыВыполненияПроверки) = Тип("Структура") Тогда
		ПроверитьПараметрВыполненияПроверки(ПараметрыВыполненияПроверки, "КонтрольВеденияУчета.ВидПроверки");
	КонецЕсли;
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр("КонтрольВеденияУчета.ВидПроверки", "ТолькоПоиск", ТолькоПоиск, Тип("Булево"));
	
	Возврат КонтрольВеденияУчетаСлужебный.ВидПроверки(ПараметрыВыполненияПроверки, ТолькоПоиск);
	
КонецФункции

// Принудительно обновляет состав проверок ведения учета при изменении метаданных
// или иных настроек.
//
Процедура ОбновитьПараметрыПроверокУчета() Экспорт
	
	Если Не ОбщегоНазначения.РазделениеВключено() Тогда
		КонтрольВеденияУчетаСлужебный.ОбновитьПараметрыПроверокУчета();
	КонецЕсли;
	
	Если КонтрольВеденияУчетаСлужебный.ЕстьИзмененияПараметровПроверокУчета() Тогда
		КонтрольВеденияУчетаСлужебный.ОбновитьВспомогательныеДанныеПоИзменениямКонфигурации();
	КонецЕсли;
	
КонецПроцедуры

#Область УстаревшиеПроцедурыИФункции

// Устарела. Следует использовать функцию ПодробнаяИнформацияПоВидамПроверок.
// Возвращает подробные сведения о выявленных проблемах указанного вида проверки.
//
// Параметры:
//   ВидПроверок                - СправочникСсылка.ВидыПроверок - ссылка на вид проверки.
//                              - Строка - строковый идентификатор вида проверки.
//                              - Массив из Строка - строковые идентификаторы вида проверки.
//   ПоискПоТочномуСоответствию - Булево - регулирует возможности точности. Если Истина, то поиск ведется
//                                по переданным свойствам на равенство, остальные свойства должны быть равны
//                                Неопределено (табличная часть дополнительных свойств должна быть пуста).
//                                Если Ложь, то значения остальных свойств могут быть произвольными, главное
//                                чтобы соответствующие свойства были равны свойствам структуры. По умолчанию Истина.
//
// Возвращаемое значение:
//   ТаблицаЗначений:
//     * ПроблемныйОбъект         - ЛюбаяСсылка - ссылка на объект "Источник" проблем.
//     * ПравилоПроверки          - СправочникСсылка.ПравилаПроверкиУчета - ссылка на выполненную проверку.
//     * УточнениеПроблемы        - Строка - строка-уточнение найденной проблемы.
//     * ВажностьПроблемы         - ПеречислениеСсылка.ВажностьПроблемыУчета - важность проблемы учета
//                                  "Информация", "Предупреждение", "Ошибка" и "ПолезныйСовет".
//     * Ответственный            - СправочникСсылка.Пользователи - заполнен если есть возможность
//                                  идентифицировать ответственного в проблемном объекте.
//     * ДополнительнаяИнформация - ХранилищеЗначения - служебное свойство с дополнительными
//                                  сведениями, связанными с выявленной проблемой.
//     * Выявлено                 - Дата - серверное время идентификации проблемы.
//
// Пример:
//   1) Результат = ПодробнаяИнформацияПоВидамПроверок("СистемныеПроверки");
//   2) ВидПроверок = Новый Массив;
//      ВидПроверок.Добавить("ЗакрытиеМесяца");
//      ВидПроверок.Добавить(Организация);
//      ВидПроверок.Добавить(МесяцЗакрытия);
//      Результат = ПодробнаяИнформацияПоВидамПроверок(ВидПроверок);
//
Функция ПодробнаяИнформацияПоВидамПроверки(ВидПроверок, ПоискПоТочномуСоответствию = Истина) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.ПодробнаяИнформацияПоВидамПроверок";
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ВидПроверок", ВидПроверок, КонтрольВеденияУчетаСлужебный.ОписаниеТипаВидПроверки());
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ПоискПоТочномуСоответствию", ПоискПоТочномуСоответствию, Тип("Булево"));
	
	ПодробнаяИнформация = Новый ТаблицаЗначений;
	МассивВидовПроверок = Новый Массив;
	
	Если ТипЗнч(ВидПроверок) = Тип("СправочникСсылка.ВидыПроверок") Тогда
		МассивВидовПроверок.Добавить(ВидПроверок);
	Иначе
		МассивВидовПроверок = КонтрольВеденияУчетаСлужебный.ВидыПроверок(ВидПроверок, ПоискПоТочномуСоответствию);
	КонецЕсли;
	
	Если МассивВидовПроверок.Количество() = 0 Тогда
		Возврат ПодробнаяИнформация;
	КонецЕсли;
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ РАЗРЕШЕННЫЕ
	|	РезультатыПроверкиУчета.ПроблемныйОбъект КАК ПроблемныйОбъект,
	|	РезультатыПроверкиУчета.ВажностьПроблемы КАК ВажностьПроблемы,
	|	РезультатыПроверкиУчета.ПравилоПроверки КАК ПравилоПроверки,
	|	РезультатыПроверкиУчета.ВидПроверки КАК ВидПроверки
	|ИЗ
	|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
	|ГДЕ
	|	НЕ РезультатыПроверкиУчета.ИгнорироватьПроблему
	|	И РезультатыПроверкиУчета.ВидПроверки В (&МассивВидовПроверок)");
	
	Запрос.УстановитьПараметр("МассивВидовПроверок", МассивВидовПроверок);
	Результат = Запрос.Выполнить();
	
	Если Не Результат.Пустой() Тогда
		ПодробнаяИнформация = Результат.Выгрузить();
	КонецЕсли;
	
	Возврат ПодробнаяИнформация;
	
КонецФункции

// Устарела. Следует использовать функцию СводнаяИнформацияПоВидамПроверок.
// Возвращает сводку по количеству выявленных проблем указанного вида проверки.
//
// Параметры:
//   ВидПроверок                - СправочникСсылка.ВидыПроверок - ссылка на вид проверки.
//                              - Строка - строковый идентификатор вида проверки.
//                              - Массив из Строка - строковые идентификаторы вида проверки.
//   ПоискПоТочномуСоответствию - Булево - регулирует возможности точности. Если Истина, то поиск ведется
//                                по переданным свойствам на равенство, остальные свойства должны быть равны
//                                Неопределено (табличная часть дополнительных свойств должна быть пуста).
//                                Если Ложь, то значения остальных свойств могут быть произвольными, главное
//                                чтобы соответствующие свойства были равны свойствам структуры. По умолчанию Истина.
//
// Возвращаемое значение:
//  Структура:
//    * Количество - Число - общее количество найденных проблем.
//    * ЕстьОшибки - Булево - признак того, имеются ли ошибки среди найденных проблем (с важностью "Ошибка").
//
// Пример:
//   1) Результат = СводнаяИнформацияПоВидамПроверок("СистемныеПроверки");
//   2) ВидПроверок = Новый Массив;
//      ВидПроверок.Добавить("ЗакрытиеМесяца");
//      ВидПроверок.Добавить(Организация);
//      ВидПроверок.Добавить(МесяцЗакрытия);
//      Результат = СводнаяИнформацияПоВидамПроверок(ВидПроверок);
//
Функция СводнаяИнформацияПоВидамПроверки(ВидПроверок, ПоискПоТочномуСоответствию = Истина) Экспорт
	
	ИмяПроцедуры = "КонтрольВеденияУчета.СводнаяИнформацияПоВидамПроверок";
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ВидПроверок", ВидПроверок, КонтрольВеденияУчетаСлужебный.ОписаниеТипаВидПроверки());
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедуры, "ПоискПоТочномуСоответствию", ПоискПоТочномуСоответствию, Тип("Булево"));
	
	СводнаяИнформация = Новый Структура;
	СводнаяИнформация.Вставить("Количество", 0);
	СводнаяИнформация.Вставить("ЕстьОшибки", Ложь);
	
	МассивВидовПроверок = Новый Массив;
	Если ТипЗнч(ВидПроверок) = Тип("СправочникСсылка.ВидыПроверок") Тогда
		МассивВидовПроверок.Добавить(ВидПроверок);
	Иначе
		МассивВидовПроверок = КонтрольВеденияУчетаСлужебный.ВидыПроверок(ВидПроверок, ПоискПоТочномуСоответствию);
		Если МассивВидовПроверок.Количество() = 0 Тогда
			Возврат СводнаяИнформация;
		КонецЕсли;
	КонецЕсли;
	
	Запрос = Новый Запрос(
	"ВЫБРАТЬ РАЗРЕШЕННЫЕ
	|	КОЛИЧЕСТВО(*) КАК Количество,
	|	ЕСТЬNULL(МАКСИМУМ(ВЫБОР
	|				КОГДА РезультатыПроверкиУчета.ВажностьПроблемы = ЗНАЧЕНИЕ(Перечисление.ВажностьПроблемыУчета.Ошибка)
	|					ТОГДА ИСТИНА
	|				ИНАЧЕ ЛОЖЬ
	|			КОНЕЦ), ЛОЖЬ) КАК ЕстьОшибки
	|ИЗ
	|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
	|ГДЕ
	|	НЕ РезультатыПроверкиУчета.ИгнорироватьПроблему
	|	И РезультатыПроверкиУчета.ВидПроверки В (&МассивВидовПроверок)");
	
	Запрос.УстановитьПараметр("МассивВидовПроверок", МассивВидовПроверок);
	Результат = Запрос.Выполнить().Выбрать();
	Результат.Следующий();
	
	ЗаполнитьЗначенияСвойств(СводнаяИнформация, Результат);
	
	Возврат СводнаяИнформация;
	
КонецФункции

#КонецОбласти

#КонецОбласти

#Область СлужебныйПрограммныйИнтерфейс

Функция ПроблемныеОбъекты(ПравилоПроверки, Смещение = Неопределено, Порция = 1000) Экспорт
	
	Запрос = Новый Запрос;
	ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ 1000
		|	РезультатыПроверкиУчета.ПроблемныйОбъект КАК ПроблемныйОбъект,
		|	РезультатыПроверкиУчета.ПравилоПроверки КАК ПравилоПроверки,
		|	РезультатыПроверкиУчета.ВидПроверки КАК ВидПроверки,
		|	РезультатыПроверкиУчета.КлючУникальности КАК КлючУникальности
		|ИЗ
		|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
		|ГДЕ
		|	РезультатыПроверкиУчета.ПравилоПроверки = &ПравилоПроверки
		|	И НЕ РезультатыПроверкиУчета.ИгнорироватьПроблему
		|	И РезультатыПроверкиУчета.ПроблемныйОбъект > &ПроблемныйОбъект
		|
		|УПОРЯДОЧИТЬ ПО
		|	РезультатыПроверкиУчета.ПроблемныйОбъект";
	
	Если Порция = 1000 Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "1000", Формат(Порция, "ЧГ=0"));
	КонецЕсли;
	Запрос.Текст = ТекстЗапроса;
	
	Если Смещение = Неопределено Тогда
		Смещение = "";
	КонецЕсли;
	
	Запрос.УстановитьПараметр("ПравилоПроверки",  ПравилоПроверки);
	Запрос.УстановитьПараметр("ПроблемныйОбъект", Смещение);
	
	Возврат Запрос.Выполнить().Выгрузить();
	
КонецФункции

Функция ПроблемныеОбъектыПоВидуПроверки(ВидПроверки, Смещение = Неопределено, Порция = 1000) Экспорт
	
	Запрос = Новый Запрос;
	ТекстЗапроса = "ВЫБРАТЬ ПЕРВЫЕ 1000
		|	РезультатыПроверкиУчета.ПроблемныйОбъект КАК ПроблемныйОбъект,
		|	РезультатыПроверкиУчета.ПравилоПроверки КАК ПравилоПроверки,
		|	РезультатыПроверкиУчета.ВидПроверки КАК ВидПроверки,
		|	РезультатыПроверкиУчета.КлючУникальности КАК КлючУникальности
		|ИЗ
		|	РегистрСведений.РезультатыПроверкиУчета КАК РезультатыПроверкиУчета
		|ГДЕ
		|	РезультатыПроверкиУчета.ВидПроверки = &ВидПроверки
		|	И НЕ РезультатыПроверкиУчета.ИгнорироватьПроблему
		|	И РезультатыПроверкиУчета.ПроблемныйОбъект > &ПроблемныйОбъект
		|
		|УПОРЯДОЧИТЬ ПО
		|	РезультатыПроверкиУчета.ПроблемныйОбъект";
	
	Если Порция = 1000 Тогда
		ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "1000", Формат(Порция, "ЧГ=0"));
	КонецЕсли;
	Запрос.Текст = ТекстЗапроса;
	
	Если Смещение = Неопределено Тогда
		Смещение = "";
	КонецЕсли;
	
	Запрос.УстановитьПараметр("ВидПроверки",      ВидПроверки);
	Запрос.УстановитьПараметр("ПроблемныйОбъект", Смещение);
	
	Возврат Запрос.Выполнить().Выгрузить();
	
КонецФункции

Процедура ОчиститьРезультатПоВидуПроверки(ПроблемныйОбъект, ВидПроверки) Экспорт
	
	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("РегистрСведений.РезультатыПроверкиУчета");
	ЭлементБлокировкиДанных.УстановитьЗначение("ПроблемныйОбъект", ПроблемныйОбъект);
	ЭлементБлокировкиДанных.УстановитьЗначение("ВидПроверки",      ВидПроверки);
	
	НачатьТранзакцию();
	
	Попытка
		
		БлокировкаДанных.Заблокировать();
		
		Набор = РегистрыСведений.РезультатыПроверкиУчета.СоздатьНаборЗаписей();
		Набор.Отбор.ПроблемныйОбъект.Установить(ПроблемныйОбъект);
		Набор.Отбор.ВидПроверки.Установить(ВидПроверки);
		Набор.Записать();
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ЗаписьЖурналаРегистрации(СобытиеЖурналаРегистрации(), УровеньЖурналаРегистрации.Ошибка,,, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
	КонецПопытки;
	
КонецПроцедуры

Процедура ОчиститьРезультатПроверки(ПроблемныйОбъект, ПравилоПроверки) Экспорт
	
	НачатьТранзакцию();
	
	Попытка
		
		БлокировкаДанных = Новый БлокировкаДанных;
		ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("РегистрСведений.РезультатыПроверкиУчета");
		ЭлементБлокировкиДанных.УстановитьЗначение("ПроблемныйОбъект", ПроблемныйОбъект);
		ЭлементБлокировкиДанных.УстановитьЗначение("ПравилоПроверки", ПравилоПроверки);
		БлокировкаДанных.Заблокировать();
		
		Набор = РегистрыСведений.РезультатыПроверкиУчета.СоздатьНаборЗаписей();
		Набор.Отбор.ПроблемныйОбъект.Установить(ПроблемныйОбъект);
		Набор.Отбор.ПравилоПроверки.Установить(ПравилоПроверки);
		Набор.Очистить();
		Набор.Записать();
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ЗаписьЖурналаРегистрации(СобытиеЖурналаРегистрации(), УровеньЖурналаРегистрации.Ошибка,,, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
	КонецПопытки;
	
КонецПроцедуры

Процедура ОчиститьРезультатПоПроверке(Проверка) Экспорт
	БлокировкаДанных = Новый БлокировкаДанных;
	ЭлементБлокировкиДанных = БлокировкаДанных.Добавить("РегистрСведений.РезультатыПроверкиУчета");
	ЭлементБлокировкиДанных.УстановитьЗначение("ПравилоПроверки", Проверка);
	
	НачатьТранзакцию();
	
	Попытка
		БлокировкаДанных.Заблокировать();
		
		Набор = РегистрыСведений.РезультатыПроверкиУчета.СоздатьНаборЗаписей();
		Набор.Отбор.ПравилоПроверки.Установить(Проверка);
		Набор.Отбор.ИгнорироватьПроблему.Установить(Ложь);
		Набор.Записать();
		
		ЗафиксироватьТранзакцию();
	Исключение
		ОтменитьТранзакцию();
		ЗаписьЖурналаРегистрации(СобытиеЖурналаРегистрации(), УровеньЖурналаРегистрации.Ошибка,,, ОбработкаОшибок.ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
	КонецПопытки;
	
КонецПроцедуры

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

Процедура ПроверитьПараметрыВыполненияПроверки(ПараметрыВыполненияПроверки, ИмяПроцедурыИлиФункции)
	
	Если ТипЗнч(ПараметрыВыполненияПроверки) = Тип("Структура") Тогда
		ПараметрыВыполнения = Новый Массив;
		ПараметрыВыполнения.Добавить(ПараметрыВыполненияПроверки);
		ПараметрыВыполненияПроверки = ПараметрыВыполнения;
	КонецЕсли;
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедурыИлиФункции, "ПараметрыВыполненияПроверки",
		ПараметрыВыполненияПроверки, Тип("Массив"));
	
	Для Каждого ПараметрПроверки Из ПараметрыВыполненияПроверки Цикл
		ПроверитьПараметрВыполненияПроверки(ПараметрПроверки, ИмяПроцедурыИлиФункции);
	КонецЦикла;

КонецПроцедуры

Процедура ПроверитьПараметрВыполненияПроверки(ПараметрПроверки, ИмяПроцедурыИлиФункции)
	
	ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедурыИлиФункции, "ПараметрыВыполненияПроверки.Элемент",
		ПараметрПроверки, Тип("Структура"));
	Для Каждого ТекущийПараметр Из ПараметрПроверки Цикл
		ОбщегоНазначенияКлиентСервер.ПроверитьПараметр(ИмяПроцедурыИлиФункции,
		ТекущийПараметр.Ключ, ТекущийПараметр.Значение, КонтрольВеденияУчетаСлужебный.ОжидаемыеТипыСвойствВидовПроверок());
	КонецЦикла;

КонецПроцедуры

Функция СобытиеЖурналаРегистрации()
	
	Возврат НСтр("ru = 'Контроль ведения учета'", ОбщегоНазначения.КодОсновногоЯзыка());
	
КонецФункции

#КонецОбласти


